home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d17 / prn2file.arc / PRN2FILE.ASM < prev    next >
Assembly Source File  |  1987-12-16  |  25KB  |  731 lines

  1. ;from VOL6N21.ARC, SEMPER BBS
  2. ;----------------------------------------------------------------------
  3. ; PRN2FILE.ASM - A resident program which redirects printer output.
  4. ; SYNTAX: PRN2FILE d:path:filename.ext [/Pn] [/Bn] [/U]
  5. ;  1)  Run PRN2FILE with the desired filename to activate it.
  6. ;  2)  Run it again with no filename to turn off redirection.
  7. ;  3)  Run it with a differant filename to change destination file.
  8. ;  4)  Use /P to designate the printer number (defaults to 1)
  9. ;  5)  Use /B to enter buffer size in K bytes (defaults to 4)
  10. ;  6)  Use /U to uninstall the program
  11. ;----------------------------------------------------------------------
  12. CSEG    SEGMENT
  13.     ASSUME    CS:CSEG,DS:NOTHING
  14.  
  15.     ORG    100H    ;Beginning for .COM programs
  16. START:    JMP Initialize    ;Initialization code is at end
  17.  
  18. ;----------------------------------------------------------------------
  19. ; Data area used by this program
  20. ;----------------------------------------------------------------------
  21. COPYRIGHT    DB    "PRN2FILE 1.0 (c) 1987 Ziff Communications Co.$",1AH
  22. PROGRAMMER    DB    "Tom Kihlken"
  23. REDIRECT_MESS    DB    "LPT"
  24. PRN_NUM        DB    "1 Redirected to: $"
  25. BAD_FILENAME    DB    "Invalid filename.$"
  26. BAD_PARAM    DB    "Usage: PRN2FILE [path][filename][/Pn][/Bnn][/U]$"
  27. BAD_ALLOC    DB    "Memory Allocation Error.$"
  28. BAD_UNINSTALL    DB    "Cannot Uninstall.$"
  29. PRN_TXT        DB    "PRN$"
  30. CRLF        DB    13,10,"$"
  31. ERR_MESSAGE    DB    13,10,"*Buffer Overflow*",13,10
  32. MESS_LENGTH    EQU    $ - OFFSET ERR_MESSAGE
  33. OLDINT08    DD    ?    ;Old timer tick interrupt vector
  34. OLDINT17    DD    ?    ;Old printer output vector
  35. OLDINT21    DD    ?    ;Old dos function interrupt vector
  36. OLDINT28    DD    ?    ;Old dos waiting interrupt vector
  37. DOS_FLAG    DD    ?    ;Dos busy flag
  38. SWITCH        DB    0    ;On/off switch for redirecting printer
  39. TIMEOUT        DW    0    ;Holds timeout counter to flush buffer
  40. INSTALLED_SEG    DW    0    ;Segment location of installed copy
  41. WRITE_FLAG    DB    0    ;Indicates buffer should be written
  42. PRINTER_NUM    DW    0    ;Default to first parallel printer
  43. BUFF_POINTER    DW      0    ;Pointer to next space in buffer
  44. BUFF_SIZE    DW    4    ;Size of buffer
  45. BUFF_SEGMENT    DW    0    ;Segment address of buffer
  46. TIME_TO_WRITE    EQU    400H    ;Flush buffer when this full
  47.  
  48. ;-----------------------------------------------------------------------
  49. ; Interrupt 17 routine. (BIOS printer output)
  50. ; If output is to the selected printer and switch is on then redirect
  51. ; the character into a file.
  52. ;-----------------------------------------------------------------------
  53. NewInt17    PROC    FAR
  54.         ASSUME    DS:NOTHING, ES:NOTHING
  55.  
  56.         CMP    DX,CS:PRINTER_NUM ;Is this the selected printer?
  57.         JNE    Ignore        ;If not, let bios handle it
  58.         CMP    CS:SWITCH,1    ;Is redirection turned on?
  59.         JE    Redirect_It    ;If on, take jump
  60. Ignore:
  61.         JMP    CS:OLDINT17    ;Jump to the bios routine
  62.  
  63. Redirect_It:
  64.         STI            ;Get interrupts back on
  65.         MOV    CS:TIMEOUT,91    ;Reset timeout counter
  66.         PUSH    SI        ;Si will be used for a pointer
  67.         CMP    AH,1        ;Initializing the printer?
  68.         JE    Write_Buff    ;If yes, then flush the buffer
  69.         OR    AH,AH        ;Printing a character?
  70.         JNZ    Print_Ret    ;If not, take jump to return
  71.         MOV    SI,CS:BUFF_POINTER    ;Get pointer to the buffer
  72.         CMP    SI,CS:BUFF_SIZE    ;Is buffer filled up yet?
  73.         JE    Print_Ret    ;If full just return.
  74.  
  75.         PUSH    DS        ;Save the data segment
  76.         MOV    DS,CS:BUFF_SEGMENT    ;Load DS with the buffer seg
  77.         MOV    DS:[SI],AL    ;Store the character in buffer
  78.         POP    DS        ;Restore data segment
  79.         INC    SI        ;And point to next position
  80.         MOV    CS:BUFF_POINTER,SI    ;Save the new pointer
  81.  
  82.         CMP    SI,TIME_TO_WRITE ;Is buffer filling up yet?
  83.         JL    Print_Ret     ;If not, just return
  84. Write_Buff:
  85.         MOV    CS:WRITE_FLAG,1    ;Signal buffer needs emptying
  86.         PUSH    DS
  87.          PUSH    BX
  88.         LDS    BX,CS:DOS_FLAG    ;Get location of dos flag
  89.         CMP    BYTE PTR [BX],0    ;Is dos busy flag set?
  90.         POP    BX
  91.         POP    DS
  92.         JNE    Print_Ret    ;If busy, do nothing
  93.          CALL    Write_To_File    ;This empties the buffer
  94. Print_Ret:
  95.         POP    SI
  96.         MOV    AH,10010000B    ;Return printer status good
  97.         IRET            ;Return from interrupt
  98. NewInt17    ENDP
  99.  
  100. ;----------------------------------------------------------------------
  101. ; New interrupt 08h (timer tick) decrement the timeout counter. Set
  102. ; the flush flag when counter reaches zero.
  103. ;----------------------------------------------------------------------
  104. NewInt08    PROC    FAR
  105.         ASSUME    DS:NOTHING, ES:NOTHING
  106.  
  107.         PUSHF            ;Simulate an interrupt
  108.         CALL    CS:OLDINT08    ;Do normal timer routine
  109.         DEC    CS:TIMEOUT    ;Count down the flush time count
  110.         JNZ    Still_Time    ;Count until it gets to zero
  111.         CMP    CS:BUFF_POINTER,0 ;Anything in buffer?
  112.         JE    Still_Time    ;If not, just continue
  113.         MOV    CS:WRITE_FLAG,1    ;Set flush trigger
  114. Still_Time:
  115.         IRET            ;Return from timer interrupt
  116.  
  117. NewInt08    ENDP
  118.  
  119. ;----------------------------------------------------------------------
  120. ; Interrupt 21 routine.  (DOS function calls) intercept function 40h
  121. ; when it writes to the printer.  Also check to see if WRITE_FLAG is
  122. ; set to one.  If it is then flush the buffer.
  123. ;----------------------------------------------------------------------
  124. NewInt21    PROC    FAR
  125.         ASSUME    DS:NOTHING, ES:NOTHING
  126.  
  127.         PUSHF            ;Save the callers flags
  128.         CMP    CS:WRITE_FLAG,1    ;Buffer need to be written?
  129.         JNE    Dont_Write    ;If not, then just return
  130.         PUSH    DS
  131.          PUSH    BX
  132.         LDS    BX,CS:DOS_FLAG    ;Get location of DOS flag
  133.         CMP    BYTE PTR [BX],0    ;Is DOS busy flag set?
  134.         POP    BX
  135.         POP    DS
  136.         JNE    Dont_Write    ;If busy, do nothing
  137.         CALL    Write_To_File    ;Empty the buffer now
  138. Dont_Write:
  139.         OR    AH,AH        ;Doing function zero?
  140.         JNE    Not_Zero
  141.         MOV    AX,4C00H    ;If yes, change it to 4Ch
  142. Not_Zero:
  143.         CMP    AH,40H        ;Writing to a device?
  144.         JNE    Not_Printer    ;If not, just continue
  145.         CMP    BX,4        ;Writing to the printer handle?
  146.         JNE    Not_Printer    ;If not, just continue
  147.         CMP    CS:SWITCH,1    ;Is redirection on?
  148.         JE    Print_It    ;If yes, then redirect it
  149. Not_Printer:
  150.         POPF            ;Recover flags from stack
  151.         CLI
  152.         JMP    CS:OLDINT21    ;Do the DOS function
  153.  
  154. ; Emulate print string function by involking INT 17h
  155.  
  156. Print_It:
  157.         STI            ;Reenable interrupts
  158.         CLD            ;String moves forward
  159.  
  160.         PUSH    CX        ;Save these registers
  161.         PUSH    DX
  162.         PUSH    SI
  163.  
  164.         MOV    SI,DX        ;Get pointer to string
  165.         MOV    DX,PRINTER_NUM    ;Selected printer ID in DX
  166.         JCXZ    End_Loop    ;Skip loop if count is zero
  167. Print_Loop:
  168.         LODSB            ;Load next character from string
  169.         MOV    AH,00        ;Print character function
  170.         INT    17H        ;BIOS print
  171.         LOOP    Print_Loop    ;Loop through whole string
  172. End_Loop:
  173.         POP    SI
  174.         POP    DX
  175.         POP    CX
  176.  
  177.         MOV    AX,CX        ;All bytes were output
  178.         POPF            ;Restore the callers flags
  179.  
  180.         CLC            ;Return success status
  181.         STI            ;Reenable interrupts
  182.         RET    2        ;Return with current flags
  183.  
  184. NewInt21    ENDP
  185.  
  186. ;----------------------------------------------------------------------
  187. ; This copies the buffer contents to a file. It should only be called
  188. ; when dos is in a reentrant condition.  All registers are preserved
  189. ;----------------------------------------------------------------------
  190. Write_To_File    PROC    NEAR
  191.         ASSUME    DS:NOTHING, ES:NOTHING
  192.  
  193.         PUSH    AX        ;Save registers we need to use
  194.         PUSH    BX
  195.         PUSH    CX
  196.         PUSH    DX
  197.         PUSH    DS
  198.         PUSH    ES
  199.  
  200.         PUSH    CS
  201.         POP    DS        ;Set DS to code segment
  202.         ASSUME    DS:CSEG        ;Tell assembler DS is CSEG
  203.         MOV    WRITE_FLAG,0    ;Clear write request flag
  204.              MOV    AX,3524H    ;Get dos critical error vector
  205.         CALL    DOS_Function    ;Do the dos function
  206.         PUSH    BX        ;Save old vector on stack
  207.         PUSH    ES
  208.  
  209. ; Replace the dos severe error interrupt with our own routine.
  210.  
  211.         MOV    DX,OFFSET NewInt24
  212.         MOV    AX,2524H    ;Setup to change int 24h vector
  213.         CALL    DOS_Function    ;Do the dos function
  214.  
  215. ; First try to open the file.  If dos returns with the carry flag set,
  216. ; the file didn't exist and we must create it.  Once the file is opened,
  217. ; advance the file pointer to the end of file to append.
  218.  
  219.         CMP    BUFF_POINTER,0    ;Anything in the buffer?
  220.         JE    Rep_Vector    ;If not, no nothing
  221.         MOV    DX,OFFSET FILENAME ;Point to filename
  222.         MOV    AX,3D02H    ;Dos function to open file
  223.         CALL    DOS_Function    ;Do the dos function
  224.         JC    File_Not_Found    ;Set if file doesn't exist.
  225.         MOV    BX,AX        ;Keep handle in BX also
  226.         XOR    CX,CX        ;Move dos file pointer to the
  227.         XOR    DX,DX        ;End of the file. this lets us
  228.         MOV    AX,4202H    ;Append this to an existing file
  229.         CALL    DOS_Function    ;Do the dos function
  230.         JC    Close_File       ;On any error, take jump
  231.         JMP    SHORT Write_File
  232.  
  233. File_Not_Found:
  234.         CMP    AX,2        ;Was it file not found error?
  235.         JNE    Rep_Vector    ;If not, just quit
  236.         MOV    CX,0020H    ;Attribute for new file
  237.         MOV    AH,3CH        ;Create file for writing
  238.         CALL    DOS_Function    ;Do the dos function
  239.         JC    Close_File      ;On any error, take jump
  240.  
  241.         MOV    BX,AX        ;Save handle in BX also
  242. Write_File:
  243.         xor    dx,dx    ;MOV    DX,0    ;Point to buffer
  244.         MOV    CX,BUFF_POINTER    ;Number of chars in buffer
  245.         MOV    AH,40H        ;Dos write to a device function
  246.         PUSH    DS
  247.         MOV    DS,BUFF_SEGMENT    ;Point to buffer segment
  248.         CALL    DOS_Function    ;Do the dos function
  249.         POP    DS
  250.         JC    Close_File      ;On any error, take jump
  251.         CMP    CX,AX        ;Was everything written
  252.         JNE    Close_File    ;If not, it was an error
  253.         CMP    CX,BUFF_SIZE    ;Was buffer full?
  254.         JNE    Close_File    ;If not everything is OK
  255.  
  256.         MOV    DX,OFFSET ERR_MESSAGE ;Insert the error message
  257.         MOV    CX,MESS_LENGTH
  258.         MOV    AH,40H        ;Dos write to file function
  259.         CALL    DOS_Function    ;Do the dos function
  260. Close_File:
  261.         MOV    AH,3EH        ;Dos function to close the file
  262.         CALL    DOS_Function    ;Do the dos function
  263. Rep_Vector:
  264.         MOV     BUFF_POINTER,0    ;Indicate buffer is empty
  265.         POP    DS        ;Recover int 24h vector from stack
  266.         POP    DX
  267.         MOV    AX,2524H    ;Restore critical error vector
  268.         CALL    DOS_Function    ;Do the dos function
  269.         ASSUME    DS:NOTHING
  270.         POP    ES        ;Restore all registers
  271.         POP     DS
  272.         POP    DX
  273.         POP    CX
  274.         POP    BX
  275.         POP    AX
  276.         RET            ;Finished with writing to disk
  277.  
  278. Write_To_File    ENDP
  279.  
  280. ;----------------------------------------------------------------------
  281. ; This routine emulates an INT 21 by calling the dos interrupt address
  282. ;----------------------------------------------------------------------
  283. DOS_Function    PROC    NEAR
  284.         ASSUME    DS:NOTHING, ES:NOTHING
  285.  
  286.         PUSHF            ;Save the processor flags
  287.         CLI            ;Clear interrupt enable bit
  288.         CALL    CS:OLDINT21    ;Execute the interupt procedure
  289.         STI            ;Enable further interrupts
  290.         RET            ;And return to calling routine
  291.  
  292. DOS_Function    ENDP
  293.  
  294. ;----------------------------------------------------------------------
  295. ; New interrupt 24h (critical dos error).  This interrupt is only in
  296. ; effect when writing to the disk.  It is required to suppress the
  297. ; 'Abort, Retry, Ignore' message.  All fatal disk errors are ignored.
  298. ;----------------------------------------------------------------------
  299. NewInt24    PROC    FAR
  300.         ASSUME    DS:NOTHING, ES:NOTHING
  301.  
  302.         STI            ;Turn interrupts back on
  303.          XOR    AL,AL        ;Tells dos to ignore the error
  304.         MOV    CS:SWITCH,AL    ;Turn off logging of output
  305.         IRET            ;And return to dos
  306.  
  307. NewInt24    ENDP
  308.  
  309. ;----------------------------------------------------------------------
  310. ; New interrupt 28h (DOS idle).  Check to see if write_flag is set to
  311. ; one. If it is, then flush the buffer
  312. ;----------------------------------------------------------------------
  313. NewInt28    PROC    FAR
  314.         ASSUME    DS:NOTHING, ES:NOTHING
  315.  
  316.         STI
  317.         CMP    CS:WRITE_FLAG,0    ;Buffer need to be written?
  318.         JE    Do_Nothing    ;If not, just continue
  319.         CALL    Write_To_File    ;Empty the buffer
  320. Do_Nothing:
  321.         JMP    CS:OLDINT28    ;Continue with old interrupt
  322.  
  323. NewInt28    ENDP
  324.  
  325. ;----------------------------------------------------------------------
  326. ; Here is the code used to initialize prn2file.com.  First determine
  327. ; if prn2file is already installed.  If it is, just copy new parameters
  328. ; into the resident programs data area, otherwise save old vectors
  329. ; and replace with new ones.  The output buffer will later overlay
  330. ; this code to conserve memory.
  331. ;----------------------------------------------------------------------
  332.         ASSUME    CS:CSEG, DS:CSEG, ES:NOTHING
  333. Initialize:
  334.         MOV    DX,OFFSET COPYRIGHT
  335.         CALL    String_CrLf    ;Display the string
  336.  
  337. ; Search for a previously installed copy of prn2file
  338.  
  339.         NOT    WORD PTR START    ;Modify to avoid false match
  340.         XOR    BX,BX        ;Start search at segment zero
  341.         MOV    AX,CS        ;Compare to this code segment
  342. Next_Segment:
  343.         INC    BX        ;Look at next segment
  344.         CMP    AX,BX        ;Until reaching this code seg
  345.         MOV    ES,BX
  346.         JE    Not_Installed
  347.         MOV    SI,OFFSET START    ;Setup to compare strings
  348.         MOV    DI,SI
  349.         MOV    CX,16        ;16 bytes must match
  350.         REP    CMPSB        ;Compare DS:SI to ES:DI
  351.         OR    CX,CX
  352.         JNZ    Next_Segment    ;If no match, try next segment
  353.         MOV    ES:SWITCH,1    ;Turn redirection on
  354.         MOV    DX,ES:PRINTER_NUM ;Retrieve old printer number
  355.         MOV    DS:PRINTER_NUM,DX ;Save it here
  356.         MOV    AH,1        ;Initialize the resident copy
  357.         INT    17H        ;To flush it's buffer
  358.         ADD    DL,31H        ;Convert printer num to ascii
  359.         MOV    PRN_NUM,DL    ;Put it into the message area
  360. Not_Installed:
  361.         MOV    INSTALLED_SEG,ES
  362.         PUSH    CS
  363.         POP    ES        ;Set ES to this segment
  364.         ASSUME    ES:CSEG
  365.         CMP    BYTE PTR DS:[0080],0 ;Anything entered?
  366.         JE    No_Params    ;If not, take jump
  367. Parse:
  368.         MOV    AL,"/"        ;Look for a slash
  369.         CALL    Load_Params
  370.         REPNE    SCASB        ;Scan for slashes
  371.         JCXZ    Parse_Done    ;Quit when no more slashes
  372.         MOV    AL,[DI]        ;Get the parameter
  373.         MOV WORD PTR [DI-1],2020H;Erase the slash and letter
  374.         OR    AL,32        ;Convert to lower case
  375.         CMP    AL,"p"        ;Is it the "p" parameter
  376.         JE    Slash_P
  377.         CMP    AL,"b"        ;Is it the "b" parameter
  378.         JE    Slash_B
  379.         CMP    AL,"u"        ;Is it the "u" parameter
  380.         JE    Slash_U
  381. Invalid_Param:
  382.         MOV    DX,OFFSET BAD_PARAM ;Point to error message
  383.         JMP    Err_Exit
  384. Slash_U:
  385.         JMP    UnInstall    ;Slash "u" means uninstall it
  386. Slash_B:
  387.         MOV    BUFF_SIZE,0    ;Zero buff size for accumulator
  388. Next_Digit:
  389.         MOV    AX,BUFF_SIZE    ;Get current buff size
  390.         MOV    BL,10
  391.         MUL    BL        ;Times 10 for next digit
  392.         INC    DI        ;Point to next digit
  393.         MOV    BL,[DI]        ;And get the next one
  394.         SUB    BL,30H        ;Convert it to binary
  395.         JC    Parse        ;If not a digit, keep parsing
  396.         CMP    BL,9
  397.         JA    Parse        ;If not a digit, keep parsing
  398.         MOV    BYTE PTR [DI]," ";Erase character from command
  399.         XOR    BH,BH
  400.         ADD    AX,BX        ;Add in this digit
  401.         MOV    BUFF_SIZE,AX    ;And save the new total
  402.         JMP    short Next_Digit
  403. Slash_P:
  404.         INC    DI        ;Point to the printer number
  405.         MOV    AL,[DI]
  406.         MOV    BYTE PTR [DI]," ";Erase this char from command
  407.         MOV    PRN_NUM,AL    ;Put it in the message area
  408.         SUB    AL,31H        ;Convert it to printer number
  409.         XOR    AH,AH        ;Make it a word
  410.         CMP    AL,3        ;Printer id must be less than 3
  411.         JAE    Invalid_Param    ;If it isn't, take jump
  412.         MOV    PRINTER_NUM,AX    ;Store the parameter
  413.         JMP    short Parse    ;Look for more parameters
  414.  
  415. No_Params:
  416.         MOV    DX,OFFSET REDIRECT_MESS ;Point to message
  417.         MOV    AH,9        ;Display the string of text
  418.         INT    21H        ;Using DOS display function
  419.         MOV    DX,OFFSET PRN_TXT ;Point to "PRN"
  420.         CALL    String_CrLf    ;Display the string
  421.         MOV    AL,0        ;Turn off redirection switch
  422.         JMP    Check_For_Install
  423.  
  424. Parse_Done:
  425.         CMP    BUFF_SIZE,1    ;Buff must be at least 1K
  426.         JB    Invalid_Param    ;If not, exit with error
  427.         CMP    BUFF_SIZE,64    ;Check for maximum buff size
  428.         JA    Invalid_Param    ;If above, exit with error
  429.         MOV    AL," "        ;Look for spaces
  430.         CALL    Load_Params
  431.         REPE    SCASB        ;Scan for non-space character
  432.         JCXZ    No_Params    ;Any letters found?
  433.  
  434.         CMP    BYTE PTR [DI],":" ;Was a drive specified?
  435.         JNE    Get_Def_Drive    ;If not, get the default drive
  436.         DEC    DI        ;Now DI points to first letter
  437.         MOV    AL,[DI]        ;Get drive letter in AL
  438.         MOV    WORD PTR [DI],2020H;Erase the drive and colon
  439.         JMP    short Store_Drive
  440.  
  441. Get_Def_Drive:
  442.         MOV    AH,19H        ;Get default drive
  443.         INT    21H
  444.         ADD    AL,65        ;Convert integer drive to ascii
  445. Store_Drive:
  446.         MOV    AH,":"        ;AL has drive, AH has colon
  447.         MOV    WORD PTR FILENAME,AX ;Store drive and colon
  448.         MOV    AL,"\"        ;Look for a backslash
  449.         MOV    FILENAME+2,AL    ;Add a backslash to filename
  450.         CALL    Load_Params
  451.         REPNE    SCASB        ;Scan for a backslash
  452.         JCXZ    Get_Def_Path    ;If no path, use current path
  453.         MOV    DI,OFFSET FILENAME+2 ;Location to store path
  454.         JMP    short Store_Path
  455.  
  456. Get_Def_Path:
  457.         MOV    DL,FILENAME    ;Selected drive letter
  458.         AND    DL,11011111B    ;Convert it to upper case
  459.         SUB    DL,64        ;Convert it to integer
  460.         MOV    SI,OFFSET FILENAME + 3 ;Put current path at SI
  461.         MOV    DI,SI        ;Save this for search later
  462.         MOV    AH,47H        ;DOS get current directory
  463.         INT    21H
  464.         JC    Bad_Name_Exit    ;Exit if invalid drive
  465.         MOV    AL,0        ;Look for end of path
  466.         CMP    [DI],AL        ;Was there any path?
  467.         JE    Store_Path    ;If not, don't scan it
  468.         MOV    CX,64        ;Maximum number of bytes in path
  469.         REPNE    SCASB        ;Scan for end of path string
  470.         MOV    BYTE PTR [DI-1],"\" ;Add the trailing backslash
  471. Store_Path:
  472.         PUSH    DI        ;Save location to append path
  473.         MOV    AL," "        ;Look for blank spaces
  474.         CALL    Load_Params
  475.         REPE    SCASB        ;Scan for non-blank character
  476.         MOV    SI,DI
  477.         DEC    SI        ;This is first letter of path
  478.         POP    DI        ;Get back location to append
  479. Copy_Path:
  480.         LODSB            ;Get next char of path
  481.         CMP    AL," "        ;Is it a blank?
  482.         JE    Verify_Name    ;If yes, its the last char
  483.         CMP    AL,13        ;Is it a carriage return?
  484.         JE    Verify_Name    ;If yes, its the last char
  485.         STOSB            ;Store this letter
  486.         JMP    short Copy_Path    ;Copy until end of path found
  487.  
  488. Verify_Name:
  489.         PUSH    DI        ;Save end of string location
  490.         MOV    BYTE PTR [DI],"$" ;Mark eos for dos display
  491.         MOV    DX,OFFSET REDIRECT_MESS ;Point to message
  492.         MOV    AH,9        ;Display the string of text
  493.         INT    21H        ;Using dos display function
  494.         MOV    DX,OFFSET FILENAME ;Point to filename for display
  495.         CALL    String_CrLf    ;Display the string
  496.         POP    DI
  497.         MOV    BYTE PTR [DI],0 ;Now make it an ascii string
  498.         MOV    DX,OFFSET FILENAME ;Dx points to the filename
  499.         MOV    AX,3D00H    ;Open this file for reading
  500.         INT    21H
  501.         JC    Open_Err    ;Error may indicate not found
  502. Close_It:
  503.         MOV    BX,AX        ;Get the handle into BX
  504.         MOV    AH,3EH        ;Close the file
  505.         INT    21H
  506.         JMP    short Filename_Ok
  507.  
  508. Open_Err:
  509.         MOV    CX,0020H    ;Attribute for new file
  510.         MOV    AH,3CH        ;Create file for writing
  511.         INT    21H        ;Dos function to create file
  512.         JNC    Close_It    ;If no error, just close it
  513. Bad_Name_Exit:
  514.         MOV    DX,OFFSET BAD_FILENAME
  515. Err_Exit:
  516.         CALL    String_CrLf    ;Display the string
  517.         INT    20H        ;Just exit to dos
  518. Filename_Ok:
  519.         MOV    ES,INSTALLED_SEG;Point to installed program
  520.         PUSH    DS:PRINTER_NUM    ;This moves the new printer
  521.         POP    ES:PRINTER_NUM    ;number to the resident copy
  522.         MOV    DI,OFFSET FILENAME ;Setup to copy the filename
  523.         MOV    SI,DI
  524.         MOV    CX,128        ;Copy entire file specification
  525.         REP    MOVSB        ;String move instruction
  526.         MOV    AL,1        ;Turn redirection on
  527. Check_For_Install:
  528.         MOV    CX,CS
  529.         CMP    CX,INSTALLED_SEG
  530.         MOV    ES,INSTALLED_SEG
  531.         MOV    ES:SWITCH,AL    ;Store the new on/off switch
  532.         JE    Install        ;If not installed yet, do it now
  533.         INT    20H        ;Otherwise terminate
  534.  
  535. ;----------------------------------------------------------------------
  536. ; This subroutine displays a string followed by a CR and LF
  537. ;----------------------------------------------------------------------
  538. String_CrLf    PROC    NEAR
  539.  
  540.         MOV    AH,9        ;Display the string of text
  541.         INT    21H        ;Using dos display function
  542.         MOV    DX,OFFSET CRLF    ;Now point to CR/LF characters
  543.         MOV    AH,9        ;Send the CR and LF
  544.         INT    21H
  545.         RET
  546.  
  547. String_CrLf    ENDP
  548.  
  549. ;----------------------------------------------------------------------
  550. ; This subroutine sets DI to the command line and CX to the byte count
  551. ;----------------------------------------------------------------------
  552. Load_Params    PROC    NEAR
  553.  
  554.         MOV    DI,80H        ;Point to parameter area
  555.         MOV    CL,CS:[DI]    ;Get number of chars into CL
  556.         XOR    CH,CH        ;Make it a word
  557.         INC    DI        ;Point to first character
  558.         CLD            ;String search forward
  559.         RET
  560.  
  561. Load_Params    ENDP
  562.  
  563. ;----------------------------------------------------------------------
  564. ; This code does the actual installation by storing the existing
  565. ; interrupt vectors and replacing them with the new ones.
  566. ; Then allocate memory for the buffer.  Exit and remain resident.
  567. ;----------------------------------------------------------------------
  568.         ASSUME    DS:CSEG, ES:CSEG
  569. Install:
  570.         MOV    BX,OFFSET END_OF_CODE    ;Get end of resident code
  571.         ADD    BX,15
  572.         MOV    CL,4        ;Shift by 4 to divide by 16
  573.         SHR    BX,CL        ;This converts to paragraphs
  574.         MOV    AH,4AH        ;Modify memory block
  575.         INT    21H        ;Dos setblock function call
  576.         JNC    Allocate_Buffer    ;If it worked ok, then continue
  577. Alloc_Error:
  578.         MOV    DX,OFFSET BAD_ALLOC ;Err message for bad allocation
  579.         JMP    Err_Exit    ;Display message and exit
  580.  
  581. Allocate_Buffer:
  582.         MOV    BX,BUFF_SIZE    ;Buffer size in K bytes
  583.         MOV    CL,6        ;Shift by 6 to get paragraphs
  584.         SHL    BX,CL        ;Buffersize is in paragraphs
  585.         MOV    AH,48H
  586.         INT    21H        ;Dos allocate memory
  587.         JC    Alloc_Error    ;If allocation error, take jump
  588.         MOV    BUFF_SEGMENT,AX    ;Save the segment for the buffer
  589.  
  590.         MOV    AX,BUFF_SIZE    ;Buffer size in K bytes
  591.         MOV    CL,10        ;Shift by 10 to get bytes
  592.         SHL    AX,CL
  593.         OR    AX,AX        ;Is buff_size=0 (64K)?
  594.         JNZ    Size_Ok
  595.         DEC    AX        ;If yes, make it FFFFh
  596. Size_Ok:
  597.         MOV    BUFF_SIZE,AX    ;Now buff_size is in bytes
  598.  
  599.         ASSUME    ES:NOTHING
  600.  
  601.         MOV    AH,34H        ;Get dos busy flag location
  602.         INT    21H
  603.         MOV    WORD PTR [DOS_FLAG]  ,BX ;Store flag address
  604.         MOV    WORD PTR [DOS_FLAG+2],ES
  605.  
  606.                MOV    AX,3508H    ;Get timer interrupt vector
  607.         INT    21H
  608.         MOV    WORD PTR [OLDINT08]  ,BX
  609.         MOV    WORD PTR [OLDINT08+2],ES
  610.         MOV    DX, OFFSET NewInt08
  611.         MOV    AX, 2508H
  612.         INT    21H        ;Dos function to change vector
  613.  
  614.                MOV    AX,3517H    ;Get printer interrupt vector
  615.         INT    21H
  616.         MOV    WORD PTR [OLDINT17]  ,BX
  617.         MOV    WORD PTR [OLDINT17+2],ES
  618.         MOV    DX, OFFSET NewInt17
  619.         MOV    AX, 2517H
  620.         INT    21H        ;Dos function to change vector
  621.  
  622.                MOV    AX,3521H    ;Get dos function vector
  623.         INT    21H
  624.         MOV    WORD PTR [OLDINT21]  ,BX
  625.         MOV    WORD PTR [OLDINT21+2],ES
  626.         MOV    DX, OFFSET NewInt21
  627.         MOV    AX, 2521H
  628.         INT    21H        ;Dos function to change vector
  629.  
  630.                MOV    AX,3528H    ;Get dos waiting vector
  631.         INT    21H
  632.         MOV    WORD PTR [OLDINT28]  ,BX
  633.         MOV    WORD PTR [OLDINT28+2],ES
  634.         MOV    DX, OFFSET NewInt28
  635.         MOV    AX, 2528H
  636.         INT    21H        ;Dos function to change vector
  637.  
  638. ;----------------------------------------------------------------------
  639. ; Deallocate our copy of the enviornment.  Exit using interrupt 27h
  640. ; (TSR). This leaves code and space for buffer resident.
  641. ;----------------------------------------------------------------------
  642.  
  643.         MOV    AX,DS:[002CH]    ;Get segment of enviornment
  644.         MOV    ES,AX        ;Put it into ES
  645.         MOV    AH,49H        ;Release allocated memory
  646.         INT    21H
  647.         MOV    DX,(OFFSET END_OF_CODE - OFFSET CSEG + 15)SHR 4
  648.         MOV    AX,3100H
  649.         INT    21H        ;Terminate and stay resident
  650.  
  651. ;----------------------------------------------------------------------
  652. ; This procedure removes PRN2FILE from memory by replacing the vectors
  653. ; and releasing the memory used for the code and buffer.
  654. ;----------------------------------------------------------------------
  655.         ASSUME    DS:CSEG, ES:NOTHING
  656. UnInstall:
  657.         MOV    AL,08H        ;Check the timer interrupt
  658.         CALL    Check_Seg    ;If changed, can't uninstall
  659.         JNE    Cant_UnInstall
  660.  
  661.         MOV    AL,17H        ;Check the printer interrupt
  662.         CALL    Check_Seg    ;If changed, can't uninstall
  663.         JNE    Cant_UnInstall
  664.  
  665.         MOV    AL,21H        ;Check dos interrupt
  666.         CALL    Check_Seg    ;If changed, can't uninstall
  667.         JNE    Cant_UnInstall
  668.  
  669.         MOV    AL,28H        ;Check dos idle interrupt
  670.         CALL    Check_Seg    ;If changed, can't uninstall
  671.         JNE    Cant_UnInstall
  672.  
  673.         MOV    ES,INSTALLED_SEG
  674.         ASSUME    DS:NOTHING, ES:NOTHING
  675.  
  676.         LDS    DX,ES:OLDINT08    ;Get original vector
  677.         MOV    AX,2508H
  678.         INT    21H        ;Dos function to change vector
  679.  
  680.         LDS    DX,ES:OLDINT17    ;Get original vector
  681.         MOV    AX,2517H
  682.         INT    21H        ;Dos function to change vector
  683.  
  684.         LDS    DX,ES:OLDINT21    ;Get original vector
  685.         MOV    AX,2521H
  686.         INT    21H        ;Dos function to change vector
  687.  
  688.         LDS    DX,ES:OLDINT28    ;Get original vector
  689.         MOV    AX,2528H
  690.         INT    21H        ;Dos function to change vector
  691.  
  692.         MOV    ES,ES:BUFF_SEGMENT;Get segment of buffer
  693.         MOV    AH,49H        ;Free its allocated memory
  694.         INT    21H
  695.         JC    Release_Err    ;If error, take jump
  696.  
  697.         MOV    ES,INSTALLED_SEG;The resident program segment
  698.         NOT    WORD PTR ES:START
  699.         MOV    AH,49H        ;Free its allocated memory
  700.         INT    21H
  701.         JC    Release_Err    ;If error, take jump
  702.         MOV    AX,4C00H
  703.         INT    21H        ;Exit to dos
  704.  
  705. Release_Err:
  706.         MOV    DX,OFFSET BAD_ALLOC ;Memory allocation error
  707.         JMP    Err_Exit    ;Exit with error message
  708. Cant_UnInstall:
  709.         MOV    DX,OFFSET BAD_UNINSTALL ;Point to error message
  710.         JMP    Err_Exit    ;Exit with error message
  711.  
  712. ;----------------------------------------------------------------------
  713. ; This subroutine checks to see if an interrupt vector points to the
  714. ; installed program segment. Returns with ZF=1 if it does.
  715. ;----------------------------------------------------------------------
  716. Check_Seg    PROC    NEAR
  717.  
  718.         MOV    AH,35H        ;Get the vector
  719.         INT    21H        ;Dos function to get the vector
  720.         MOV    AX,ES
  721.         CMP    AX,INSTALLED_SEG;Is it the installed segment?
  722.         RET
  723.  
  724. Check_Seg    ENDP
  725. ;----------------------------------------------------------------------
  726. FILENAME    LABEL    BYTE        ;File name will go here
  727. END_OF_CODE    =    $ + 128        ;Allow 128 bytes for it
  728.  
  729. CSEG        ENDS
  730.         END    START
  731.